package com.sakura.oj.sandbox.template;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.github.dockerjava.api.DockerClient;
import com.sakura.oj.sandbox.CodeSandbox;
import com.sakura.oj.sandbox.enums.CodeSandBoxStatusEnums;
import com.sakura.oj.sandbox.model.ExecuteCodeRequest;
import com.sakura.oj.sandbox.model.ExecuteCodeResponse;
import com.sakura.oj.sandbox.model.RunInfo;
import com.sakura.oj.sandbox.utils.SpringContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 代码沙箱抽象类 - 使用模板方法模式定义代码执行流程
 */
@Slf4j
public abstract class AbstractCodeSandbox implements CodeSandbox {

    // 日志配置
    private static final Logger dockerLogger = (Logger) LoggerFactory.getLogger("com.github.dockerjava");
    private static final Logger clientLogger = (Logger) LoggerFactory.getLogger("org.apache.hc.client5.http");

    static {
        // 设置 docker java的日志等级
        dockerLogger.setLevel(Level.INFO);
        clientLogger.setLevel(Level.INFO);
    }


    /**
     * 模板方法，定义了代码执行的通用流程
     *
     * @param executeCodeRequest 执行代码请求
     * @return 执行代码响应
     */
    @Override
    public final ExecuteCodeResponse executeCode(ExecuteCodeRequest executeCodeRequest) {
        ExecuteCodeResponse response = new ExecuteCodeResponse();

        try {
            // 1. 安全检查
            if (isCodeDangerous(executeCodeRequest.getCode())) {
                return getErrorResponse("代码包含危险内容", CodeSandBoxStatusEnums.DANGEROUS_CODE);
            }

            // 2. 文件准备
            String workDir = prepareWorkDirectory();
            File codeFiles = saveCodeToFile(executeCodeRequest.getCode(), workDir);

            // 3. 编译代码（如果需要）
            if (needCompile()) {
                try {
                    compileCode(codeFiles, workDir);
                } catch (Exception e) {
                    return getErrorResponse("编译错误: " + e.getMessage(), CodeSandBoxStatusEnums.COMPILE_FAILED);
                }
            }

            // 4. 执行代码
            List<String> inputList = executeCodeRequest.getInputList();
            List<String> outputList = new ArrayList<>();
            List<RunInfo> runInfoList = new ArrayList<>();
            String containerId = null;
            try {
                // 创建容器
                containerId = createAndStartContainer(workDir);

                for (String input : inputList) {
                    // 执行代码
                    ExecutionResult result = runCode(containerId, input);
                    // 收集结果 - 标准化输出，去除尾部换行符
                    outputList.add(normalizeOutput(result.output()));

                    RunInfo runInfo = new RunInfo();
                    runInfo.setTime(result.time());
                    runInfo.setMemory(result.memoryUsageKb());
                    if (result.hasError()) {
                        // 如果有error，则什么代码存在错误，下面的用例也不需要运行，直接返回运行失败即可
                        response.setStatus(CodeSandBoxStatusEnums.RUNTIME_ERROR.getValue());
                        response.setMessage(result.errorMessage());
                        response.setRunInfoList(runInfoList);
                        return response;
                    }
                    runInfoList.add(runInfo);
                }

                // 设置成功响应
                response.setOutputList(outputList);
                response.setRunInfoList(runInfoList);
                response.setStatus(CodeSandBoxStatusEnums.ACCEPTED.getValue());
                response.setMessage("ACCEPTED");

            } catch (Exception e) {
                log.error("代码沙箱执行异常", e);
                return getErrorResponse("运行错误: " + e.getMessage(), CodeSandBoxStatusEnums.RUNTIME_ERROR);
            } finally {
                // 5. 清理资源
                cleanupResources(workDir, containerId);
            }

        } catch (Exception e) {
            log.error("代码沙箱执行异常", e);
            return getErrorResponse("系统错误: " + e.getMessage(), CodeSandBoxStatusEnums.FAILED);
        }

        return response;
    }

    /**
     * 创建并启动容器
     */
    protected abstract String createAndStartContainer(String codeDirPath);

    /**
     * 在容器中执行代码
     *
     * @param containerId 容器id
     * @param input       输入用例
     * @return 执行结果
     */
    protected abstract ExecutionResult runCode(String containerId, String input) throws InterruptedException, IOException;

    /**
     * 检查代码是否包含危险内容
     */
    protected abstract boolean isCodeDangerous(String code);

    /**
     * 准备工作目录
     */
    protected abstract String prepareWorkDirectory();

    /**
     * 将代码保存到文件
     */
    protected abstract File saveCodeToFile(String code, String workDir);

    /**
     * 是否需要编译
     */
    protected abstract boolean needCompile();

    /**
     * 编译代码
     */
    protected abstract void compileCode(File codeFiles, String workDir) throws Exception;

    /**
     * 清理资源
     */
    protected void cleanupResources(String workDir, String containerId) {
        if (FileUtil.exist(workDir)) {
            FileUtil.del(workDir);
            log.info("清理工作目录: {}", workDir);
        }
        if (StringUtils.hasText(containerId)) {
            cleanupContainer(containerId);
        }
        log.info("清理资源完成");
    }

    ;

    /**
     * 生成错误响应
     */
    protected ExecuteCodeResponse getErrorResponse(String message, CodeSandBoxStatusEnums status) {
        ExecuteCodeResponse response = new ExecuteCodeResponse();
        response.setStatus(status.getValue());
        response.setMessage(message);
        return response;
    }

    /**
     * 代码执行结果记录
     *
     * @param output        输出结果
     * @param errorMessage  错误信息
     * @param memoryUsageKb 内存使用量(KB)
     */
    public record ExecutionResult(String output, String errorMessage, long memoryUsageKb, long time) {
        /**
         * 是否有错误信息
         */
        public boolean hasError() {
            return errorMessage != null && !errorMessage.isEmpty();
        }
    }

    /**
     * 标准化输出结果，去除尾部换行符和空白
     */
    protected String normalizeOutput(String output) {
        if (output == null) {
            return "";
        }

        // 去除尾部的换行符和空白字符
        return output.stripTrailing();
    }

    /**
     * 清理容器
     */
    protected void cleanupContainer(String containerId) {
        try {
            // 使用上下文工具类获取 dockerClientBean
            DockerClient dockerClient = SpringContextHolder.getBean(DockerClient.class);
            dockerClient.stopContainerCmd(containerId).exec();
            dockerClient.removeContainerCmd(containerId).exec();
            log.info("容器已停止并移除: {}", containerId);
        } catch (Exception e) {
            log.error("清理容器失败: {}", e.getMessage());
        }
    }
} 